use super::cbase::CBase;
use super::{
    err, err_value, get_db, json, ok, throw_err, Base, Client, ECode, MapSv, Resp, VBase, VMap,
    Value,
};
use std::collections::HashMap;

pub struct Util<'a> {
    client: &'a Client,
}

impl Base for Util<'_> {}

impl CBase for Util<'_> {}

impl<'a> Util<'a> {
    pub async fn do_cmd(&self, cmd: &str, data: &MapSv) -> Resp {
        match cmd {
            "col" => {
                let db = get_db(data);
                let tab = data.ck_str_trim("n")?;
                self.do_col(tab, &db).await
            }
            "tag" => {
                let db = get_db(data);
                let tab = data.ck_str_trim("n")?;
                let body = self.client.tag(tab, &db).await?;
                self.tag(&body)
            }
            "field" => {
                let db = get_db(data);
                let tab = data.ck_str_trim("n")?;
                let body = self.client.field(tab, &db).await?;
                self.field(body)
            }
            "tab" => {
                let db = get_db(data);
                let body = self.client.table(&db).await?;
                self.build_body(body)
            }
            "rmtab" => {
                let tab = data.ck_str_trim("n")?;
                let tab: Vec<&str> = tab.split(",").map(|x| x.trim()).collect();
                let db = get_db(data);
                for it in tab.iter() {
                    self.client.drop_table(it, &db).await?;
                }

                ok!()
            }
            _ => throw_err!(ECode::NoServe, "no serve"),
        }
    }

    async fn do_col(&self, tab: &str, db: &str) -> Resp {
        let mut ret = HashMap::new();

        if let Ok(body) = self.client.tag(tab, db).await {
            let body = body.ck_first("results")?;
            let body = body.ck_array("series")?;

            for it in body.iter() {
                let val = it.ck_array("values")?;

                for it in val.iter() {
                    let it = it.ckarray()?;
                    let name = it[0].ckstr()?;
                    ret.insert(name.to_string(), "string".to_string());
                }
            }
        }

        if let Ok(body) = self.client.field(tab, db).await {
            let body = body.ck_first("results")?;
            let body = body.ck_array("series")?;

            for it in body.iter() {
                let val = it.ck_array("values")?;

                for it in val.iter() {
                    let t = it.ckarray()?;

                    let name = t[0].ckstr()?;
                    let tp = t[1].ckstr()?;

                    ret.insert(name.to_string(), tp.to_string());
                }
            } // for
        }

        ok!(ret)
    }

    fn build_body(&self, body: Value) -> Resp {
        let body = body
            .ck_first("results")?
            .ck_first("series")?
            .ck_array("values")?;

        ok!(body)
    }

    fn field(&self, body: Value) -> Resp {
        let body = body.ck_first("results")?.ck_array("series")?;

        let mut ret = HashMap::new();

        for it in body.iter() {
            let it = it.ckobj()?;
            let name = it.ck_str("name")?;
            let val = it.ck_array("values")?;

            let mut tmp = Vec::new();

            for it in val.iter() {
                let t = it.ckarray()?;

                let tp = t[1].ckstr()?;
                if tp == "string" {
                    continue;
                }

                tmp.push(&t[0]);
            }

            if tmp.is_empty() {
                continue;
            }

            ret.insert(name, tmp);
        } // for

        if ret.is_empty() {
            throw_err!(ECode::EmptyData, "no data");
        }

        ok!(ret)
    }

    fn tag(&self, body: &Value) -> Resp {
        let body = body.ck_first("results")?.ck_array("series")?;

        let mut ret = HashMap::new();

        for it in body.iter() {
            let it = it.ckobj()?;
            let name = it.ck_str("name")?;
            let val = it.ck_array("values")?;

            let mut tmp = Vec::new();
            for it in val.iter() {
                let it = it.ckarray()?;
                tmp.push(&it[0]);
            }

            if tmp.is_empty() {
                continue;
            }

            ret.insert(name, tmp);
        }

        if ret.is_empty() {
            throw_err!(ECode::EmptyData, "tag empty");
        }

        ok!(ret)
    }

    pub fn new(client: &'a Client) -> Self {
        Util { client }
    }
}
